﻿using BUGCOME.Common;
using BUGCOME.Infrastructure.Attribute;
using BUGCOME.Model.Auth;
using BUGCOME.Model.Auth.dto;
using BUGCOME.ServiceCore.Services.IServices;

namespace BUGCOME.ServiceCore.Services
{
    /// <summary>
    /// 菜单服务实现类
    /// </summary>
    [AppService(ServiceType = typeof(IMenuService), ServiceLifetime = LifeTime.Transient)]
    public class SysMenuService : BaseService<Menu>, IMenuService
    {
        /// <summary>
        /// 角色服务
        /// </summary>
        public IRoleService roleService;

        /// <summary>
        /// 注入角色服务
        /// </summary>
        /// <param name="roleService">角色服务</param>
        public SysMenuService(IRoleService roleService)
        {
            this.roleService = roleService;
        }

        #region  将菜单列表转换为路由列表
        /// <summary>
        /// 将菜单列表转换为路由列表
        /// </summary>
        public List<RouterVo> BuildMenus(List<Menu> menus)
        {
            List<RouterVo> routers = new();
            if (menus == null) return routers;

            foreach (var menu in menus)
            {
                RouterVo router = new()
                {
                    Query = menu.Query,
                    Hidden = "1".Equals(menu.Visible),
                    Name = GetRouteName(menu),
                    Path = GetRoutePath(menu),
                    Component = GetComponent(menu),
                    Meta = new Meta(menu.MenuName, menu.Icon, "1".Equals(menu.IsCache), menu.MenuNameKey, menu.Path, menu.CreateTime)
                };

                List<Menu> cMenus = menu.Children;
                // 目录且有子菜单
                if (cMenus != null && cMenus.Count > 0 && UserConstants.TYPE_DIR.Equals(menu.MenuType))
                {
                    router.AlwaysShow = true;
                    router.Redirect = "noRedirect";
                    router.Children = BuildMenus(cMenus);
                }
                // 菜单内部跳转
                else if (IsMeunFrame(menu))
                {
                    router.Meta = null;
                    List<RouterVo> childrenList = new();
                    RouterVo children = new()
                    {
                        Query = menu.Query,
                        Path = menu.Path,
                        Component = menu.Component,
                        Name = string.IsNullOrEmpty(menu.Path) ? "" : menu.Path.ToLower(),
                        Meta = new Meta(menu.MenuName, menu.Icon, "1".Equals(menu.IsCache), menu.MenuNameKey, menu.Path, menu.CreateTime)
                    };
                    childrenList.Add(children);
                    router.Children = childrenList;
                }
                // 一级内链
                else if (menu.ParentId == 0 && IsInnerLink(menu))
                {
                    router.Meta = new Meta(menu.MenuName, menu.Icon);
                    router.Path = "/";
                    List<RouterVo> childrenList = new();
                    RouterVo children = new();
                    string routerPath = InnerLinkReplaceEach(menu.Path);
                    children.Query = menu.Query;
                    children.Path = routerPath;
                    children.Component = UserConstants.INNER_LINK;
                    children.Name = routerPath.ToLower();
                    children.Meta = new Meta(menu.MenuName, menu.Icon, menu.Path);
                    childrenList.Add(children);
                    router.Children = childrenList;
                }
                routers.Add(router);
            }

            return routers;
        }

        /// <summary>
        /// 内链域名特殊字符替换
        /// </summary>
        public string InnerLinkReplaceEach(string path)
        {
            return path.IsNotEmpty() ? path
                .Replace(UserConstants.HTTP, "")
                .Replace(UserConstants.HTTPS, "")
                .Replace(UserConstants.WWW, "")
                .Replace(".", "/") : path;
        }

        /// <summary>
        /// 获取路由名称
        /// </summary>
        public string GetRouteName(Menu menu)
        {
            string routerName = menu.Path.ToLower();
            // 非外链且一级目录(目录类型)
            if (IsMeunFrame(menu))
            {
                routerName = string.Empty;
            }
            return routerName;
        }

        /// <summary>
        /// 获取路由路径
        /// </summary>
        public string GetRoutePath(Menu menu)
        {
            string routerPath = menu.Path;
            // 内链打开外网
            if (menu.ParentId != 0 && IsInnerLink(menu))
            {
                routerPath = InnerLinkReplaceEach(routerPath);
            }
            // 非外链且一级目录(目录类型)
            if (0 == menu.ParentId && UserConstants.TYPE_DIR.Equals(menu.MenuType)
                && UserConstants.NO_FRAME.Equals(menu.IsFrame))
            {
                routerPath = "/" + menu.Path;
            }
            // 非外链且一级目录(菜单类型)
            else if (IsMeunFrame(menu))
            {
                routerPath = "/";
            }
            return routerPath;
        }

        /// <summary>
        /// 获取组件名称
        /// </summary>
        public string GetComponent(Menu menu)
        {
            string component = UserConstants.LAYOUT;
            if (!string.IsNullOrEmpty(menu.Component) && !IsMeunFrame(menu))
            {
                component = menu.Component;
            }
            else if (menu.Component.IsEmpty() && menu.ParentId != 0 && IsInnerLink(menu))
            {
                component = UserConstants.INNER_LINK;
            }
            else if (string.IsNullOrEmpty(menu.Component) && IsParentView(menu))
            {
                component = UserConstants.PARENT_VIEW;
            }
            return component;
        }

        /// <summary>
        /// 是否为parent_view组件
        /// </summary>
        public bool IsParentView(Menu menu)
        {
            return menu.ParentId != 0 && UserConstants.TYPE_DIR.Equals(menu.MenuType);
        }

        /// <summary>
        /// 是否为菜单内部跳转
        /// </summary>
        public bool IsMeunFrame(Menu menu)
        {
            return menu.ParentId == 0 && UserConstants.TYPE_MENUS.Contains(menu.MenuType)
                && menu.IsFrame.Equals(UserConstants.NO_FRAME);
        }

        /// <summary>
        /// 是否为内链组件
        /// </summary>
        public bool IsInnerLink(Menu menu)
        {
            return menu.IsFrame.Equals(UserConstants.NO_FRAME) && Tools.IsUrl(menu.Path);
        }

        #endregion
       
        /// <summary>
        /// 根据用户ID查权限标识集合（缓存10分钟）
        /// </summary>
        public async Task<IEnumerable<string>> SelectMenuPermsByUserIdAsync(long userId)
        {
            var menus = await Context.Queryable<Menu, RoleMenu, UserRole, Role>((m, rm, ur, r) => new JoinQueryInfos(
            JoinType.Left, m.MenuId == rm.MenuId,
            JoinType.Left, rm.RoleId == ur.RoleId,
            JoinType.Left, ur.RoleId == r.RoleId
            ))
            .WithCache(60 * 10)
            .Where((m, rm, ur, r) => m.Status == "0" && r.Status == 0 && ur.UserId == userId)
            .Select((m, rm, ur, r) => m).ToListAsync();

            var menuList = menus.Where(f => !string.IsNullOrEmpty(f.Perms));
            return menuList.Select(x => x.Perms).Distinct().ToList();
        }

        /// <summary>
        /// 根据用户ID查菜单树（管理员全量，非管理员按角色过滤）
        /// </summary>
        public async Task<List<Menu>> SelectMenuTreeByUserIdAsync(long uid)
        {
            MenuQueryDto dto = new() { Status = "0", MenuTypeIds = "M,C,L" };
            if (await roleService.IsAdminAsync(uid))
            {
                return await SelectTreeMenuListAsync(dto);
            }
            else
            {
                List<long> roleIds = await roleService.SelectUserRolesAsync(uid);
                return await SelectTreeMenuListByRolesAsync(dto, roleIds);
            }
        }

        /// <summary>
        /// 按条件查所有菜单树
        /// </summary>
        public async Task<List<Menu>> SelectTreeMenuListAsync(MenuQueryDto menu)
        {
            int parentId = menu.ParentId != null ? (int)menu.ParentId : 0;

            var list = await Queryable()
                .WhereIF(!string.IsNullOrEmpty(menu.MenuName), it => it.MenuName.Contains(menu.MenuName))
                .WhereIF(!string.IsNullOrEmpty(menu.Visible), it => it.Visible == menu.Visible)
                .WhereIF(!string.IsNullOrEmpty(menu.Status), it => it.Status == menu.Status)
                .WhereIF(!string.IsNullOrEmpty(menu.MenuTypeIds), it => menu.MenuTypeIdArr.Contains(it.MenuType))
                .WhereIF(menu.ParentId != null, it => it.ParentId == menu.ParentId)
                .OrderBy(it => new { it.ParentId, it.OrderNum })
                .ToTreeAsync(it => it.Children, it => it.ParentId, parentId);

            return list;
        }

        /// <summary>
        /// 按角色+条件查菜单树
        /// </summary>
        public async Task<List<Menu>> SelectTreeMenuListByRolesAsync(MenuQueryDto menu, List<long> roles)
        {
            var roleMenus = await Context.Queryable<RoleMenu>()
                .Where(r => roles.Contains(r.RoleId))
                .Select(f => f.MenuId).Distinct().ToListAsync();

            return await Queryable()
                .Where(c => roleMenus.Contains(c.MenuId))
                .WhereIF(!string.IsNullOrEmpty(menu.MenuName), c => c.MenuName.Contains(menu.MenuName))
                .WhereIF(!string.IsNullOrEmpty(menu.Visible), c => c.Visible == menu.Visible)
                .WhereIF(!string.IsNullOrEmpty(menu.Status), c => c.Status == menu.Status)
                .WhereIF(!string.IsNullOrEmpty(menu.MenuTypeIds), c => menu.MenuTypeIdArr.Contains(c.MenuType))
                .OrderBy(c => new { c.ParentId, c.OrderNum })
                .ToTreeAsync(it => it.Children, it => it.ParentId, 0);
        }

        public async Task<List<Menu>> SelectTreeMenuListAsync(MenuQueryDto menu, long userId)
        {
            if (menu.ParentId != null)
            {
                return await GetMenusByMenuIdAsync(menu.ParentId.ParseToInt(), userId);
            }
            List<Menu> menuList = BuildMenuTree(await SelectMenuListAsync(menu, userId));
            return menuList;
        }

        /// <summary>
        /// 构建前端所需要下拉树结构
        /// </summary>
        /// <param name="menus">菜单列表</param>
        /// <returns>下拉树结构列表</returns>
        public List<Menu> BuildMenuTree(List<Menu> menus)
        {
            List<Menu> returnList = new();
            List<long> tempList = menus.Select(f => f.MenuId).ToList();

            foreach (var menu in menus)
            {
                // 如果是顶级节点, 遍历该父节点的所有子节点
                if (!tempList.Contains(menu.ParentId))
                {
                    var menuInfo = menus.Find(f => f.MenuId == menu.MenuId);
                    //移除按钮没有上级
                    if (!tempList.Contains(menuInfo.ParentId) && menu.MenuType != "F")
                    {
                        RecursionFn(menus, menu);
                        returnList.Add(menu);
                    }
                }
            }
            if (!returnList.Any())
            {
                returnList = menus;
            }
            return returnList;
        }

        /// <summary>
        /// 获取所有菜单列表
        /// </summary>
        /// <returns></returns>
        public async Task<List<Menu>> SelectMenuListAsync(MenuQueryDto menu, long userId)
        {
            List<Menu> menuList;
            if (await roleService.IsAdminAsync(userId))
            {
                menuList = await SelectMenuListAsync(menu);
            }
            else
            {
                var userRoles = await roleService.SelectUserRolesAsync(userId);
                menuList =await SelectMenuListByRolesAsync(menu, userRoles);
            }
            return menuList;
        }

        /// <summary>
        /// 获取所有菜单
        /// </summary>
        /// <returns></returns>
        private async Task<List<Menu>> SelectMenuListAsync(MenuQueryDto menu)
        {
            var menuExp = Expressionable.Create<Menu>();
            menuExp.AndIF(!string.IsNullOrEmpty(menu.MenuName), it => it.MenuName.Contains(menu.MenuName));
            menuExp.AndIF(!string.IsNullOrEmpty(menu.Visible), it => it.Visible == menu.Visible);
            menuExp.AndIF(!string.IsNullOrEmpty(menu.Status), it => it.Status == menu.Status);
            menuExp.AndIF(!string.IsNullOrEmpty(menu.MenuTypeIds), it => menu.MenuTypeIdArr.Contains(it.MenuType));
            menuExp.AndIF(menu.ParentId != null, it => it.ParentId == menu.ParentId);

            return await Queryable()
            .Where(menuExp.ToExpression())
            .OrderBy(it => new { it.ParentId, it.OrderNum })
            .ToListAsync();
        }

        private async Task<List<Menu>> SelectMenuListByRolesAsync(MenuQueryDto menu, List<long> roles)
        {
            var roleMenus = Context.Queryable<RoleMenu>()
                 .Where(r => roles.Contains(r.RoleId));

            return await Queryable()
                .InnerJoin(roleMenus, (c, j) => c.MenuId == j.MenuId)
                .Where((c, j) => c.Status == "0")
                .WhereIF(!string.IsNullOrEmpty(menu.MenuName), (c, j) => c.MenuName.Contains(menu.MenuName))
                .WhereIF(!string.IsNullOrEmpty(menu.Visible), (c, j) => c.Visible == menu.Visible)
                .OrderBy((c, j) => new { c.ParentId, c.OrderNum })
                .Select(c => c)
                .ToListAsync();
        }

        /// <summary>
        /// 根据菜单id获取菜单列表
        /// </summary>
        /// <param name="menuId"></param>
        /// <param name="userId"></param>
        /// <returns></returns>
        public async Task<List<Menu>> GetMenusByMenuIdAsync(int menuId, long userId)
        {
            var menuExpression = Expressionable.Create<Menu>();
            menuExpression.And(c => c.ParentId == menuId);

            if (!(await roleService.IsAdminAsync(userId)))
            {
                var userRoles = await roleService.SelectUserRolesAsync(userId);
                var roleMenus = Context.Queryable<RoleMenu>()
                    .Where(r => userRoles.Contains(r.RoleId)).Select(s => s.MenuId).ToList();

                menuExpression.And(c => roleMenus.Contains(c.MenuId));
            }
            var list = GetList(menuExpression.ToExpression()).OrderBy(f => f.OrderNum).ToList();
            Context.ThenMapper(list, item =>
            {
                item.SubNum = Context.Queryable<Menu>().SetContext(x => x.ParentId, () => item.MenuId, item).Count;
            });
            return list;
        }


        /// <summary>根据菜单ID异步获取菜单</summary>
        /// <param name="menuId">菜单ID</param>
        /// <returns>菜单信息</returns>
        public async Task<Menu> GetMenuByMenuIdAsync(long menuId)
        {
            return await GetFirstAsync(it => it.MenuId == menuId);
        }

        /// <summary>异步编辑菜单（处理图标为空的情况并执行更新）</summary>
        /// <param name="menu">要编辑的菜单实体</param>
        /// <returns>包含更新操作受影响行数的异步任务</returns>
        public Task<long> EditMenuAsync(Menu menu)
        {
            // 处理图标为空的情况，设为默认空字符串
            menu.Icon = string.IsNullOrEmpty(menu.Icon) ? "" : menu.Icon;
            return UpdateAsync(menu, false);
        }


        /// <summary>根据菜单ID删除所有相关菜单（含子菜单及关联）</summary>
        /// <param name="menuId">菜单ID</param>
        /// <returns>1成功，0失败</returns>
        public async Task<long> DeleteAllMenuByIdAsync(long menuId)
        {
            var childMenu = Queryable().ToChildList(x => x.ParentId, menuId).Select(x => x.MenuId);
            var result = await UseTranAysnc(async () =>
            {
                await DeleteAsync(childMenu.ToArray(), "删除菜单");
                Context.Deleteable<RoleMenu>().Where(f => childMenu.Contains(f.MenuId)).ExecuteCommand();
            });
            return result.IsSuccess ? 1 : 0;
        }

        /// <summary>按ID删除菜单</summary>
        /// <param name="menuId">菜单ID</param>
        /// <returns>被删除的菜单ID</returns>
        public async Task<long> DeleteMenuByIdAsync(long menuId)
        {
            return await DeleteAsync(menuId);
        }

        /// <summary>检查指定菜单是否有子菜单</summary>
        /// <param name="menuId">菜单ID</param>
        /// <returns>是否有子菜单</returns>
        public async Task<bool> HasChildByMenuIdAsync(long menuId)
        {
            return await CountAsync(it => it.ParentId == menuId) > 0;
        }

        /// <summary>
        /// 异步添加菜单
        /// </summary>
        /// <param name="menu">菜单实体</param>
        /// <returns>新增菜单的ID</returns>
        public async Task<long> AddMenuAsync(Menu menu)
        {
            menu.CreateTime = DateTime.Now;
            return await InsertReturnBigIdentityAsync(menu);
        }

        /// <summary>
        /// 异步检查菜单名称唯一性（同parentId下）
        /// </summary>
        /// <param name="menu">待检查的菜单</param>
        /// <returns>唯一标识（UNIQUE）或不唯一标识（NOT_UNIQUE）</returns>
        public async Task<string> CheckMenuNameUniqueAsync(Menu menu)
        {
            long menuId = menu.MenuId == 0 ? -1 : menu.MenuId;
            Menu info = await GetFirstAsync(it => it.MenuName == menu.MenuName && it.ParentId == menu.ParentId);

            
            if (info != null && info.MenuId != menu.MenuId)
            {
                return UserConstants.NOT_UNIQUE;
            }
            return UserConstants.UNIQUE;
        }


        #region 方法

        /// <summary>
        /// 递归列表
        /// </summary>
        /// <param name="list"></param>
        /// <param name="t"></param>
        private void RecursionFn(List<Menu> list, Menu t)
        {
            //得到子节点列表
            List<Menu> childList = GetChildList(list, t);
            t.Children = childList;
            foreach (var item in childList)
            {
                if (GetChildList(list, item).Count() > 0)
                {
                    RecursionFn(list, item);
                }
            }
        }

        /// <summary>
        /// 递归获取子菜单
        /// </summary>
        /// <param name="list">所有菜单</param>
        /// <param name="sysMenu"></param>
        /// <returns></returns>
        private List<Menu> GetChildList(List<Menu> list, Menu sysMenu)
        {
            return list.Where(p => p.ParentId == sysMenu.MenuId).ToList();
        }

       

        #endregion
    }

}